import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.parser.Tag;
import org.jsoup.select.Elements;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;
import java.util.TreeMap;
import java.util.regex.Pattern;

/**
 * Created by Bella on 13/04/2014.
 */
public class PageNode {

   private String nodeId;
   private Set<String> stringLinks;
   private Set<PageNode> inPageNode;
   private Set<PageNode> outPageNode;
   private int pageLength;
   private TreeMap<String, Integer> wordsInPage; //word and number of times appearing in web page
   private String webPageContent;
   Elements firstParagraph;



    //constructor
    public PageNode(String domain) {
        firstParagraph= new Elements();
        nodeId= domain;
        stringLinks= new HashSet<String>();
        inPageNode= new HashSet<PageNode>();
        outPageNode= new HashSet<PageNode>();
        updateWordsInPageAndPageLength(domain);
        webPageContent= getWebPageContent(domain);
        findLinksInPage();


    }

    /**
     * This function adds a new link to the web site
     * @param pageNode
     */
    public void addLink(PageNode pageNode) {
        stringLinks.add(pageNode.getNodeId());
    }

    /**
     * for a url this function parses the content of the web page.
     * @param address
     * @return a string of the web page content
     */
    public String getWebPageContent(String address) {

        String wordsInLine;
        StringBuilder stringbuilder = new StringBuilder();

        try {
            URL url = new URL(address);
            BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()));


            while ((wordsInLine = br.readLine()) != null) {
                stringbuilder.append(wordsInLine);
                stringbuilder.append(System.getProperty("line.separator"));
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

        return stringbuilder.toString();
    }

    /**
     * this function receives an address of a web page and updates the fields wordsInPage and pageLength.
     * @param address
     * @return
     */
    private void updateWordsInPageAndPageLength(String address) {

        String urlContent =  getWebPageContent(address);
        String [] wordsArray = urlContent.split(" +"); //TODO handle end of sentence (,.)
        pageLength = wordsArray.length;
        wordsInPage= new TreeMap<String, Integer>();
        //for every word in page
        for (int i = 0; i < pageLength; i++) {
            //if we already found this word, increase its counter
            if (wordsInPage!=null && wordsInPage.containsKey(wordsArray[i])) {
                int value = wordsInPage.get(wordsArray[i]);
                value ++;
                wordsInPage.put(wordsArray[i], value);
            }
            //otherwise
            else {
                wordsInPage.put(wordsArray[i], 1);
            }

        }

    }

    private void findLinksInPage () {
        String newLink = "http://simple.wikipedia.org";
        Document document = Jsoup.parse(webPageContent);
        Element allParagraphs= document.select("div#mw-content-text").get(0);

        if (allParagraphs == null) {
            return;
        }
        Tag pTag = Tag.valueOf("p");

        int i = 0;
        //find the first p tag
        while (i <= allParagraphs.children().size() && !allParagraphs.child(i).tag().equals(pTag) ){
            i++;
        }
        while (i <= allParagraphs.children().size() && allParagraphs.child(i).tag().equals(pTag) ) {

            firstParagraph.add(allParagraphs.child(i));
            i++;
        }

        if (firstParagraph.isEmpty()) {
            return;
        }
        for (Element elem: firstParagraph) {

        Elements linksInPage = elem.getElementsByTag("a");
        String textInLink;

        for (Element e : linksInPage) {
            textInLink = e.attr("href");
            if (textInLink.startsWith("/wiki/")) {
                String url = newLink + textInLink;
                stringLinks.add(url);
            }
        }

        }

    }

   public boolean isRoyal() {
       Document document = Jsoup.parse(webPageContent);
       Elements elements= document.select("table.infobox");
       if (!elements.isEmpty()) { //info box exist
           if (!hasField(elements.get(0),"Born"))
               return false;

           if (belongsToRoyalFamily(elements.get(0))) { //check if the data in the info box indicates that the member is human and royal
                return true;
               }
           }
       if (isHuman()) {
           Element catLinks = document.getElementById("mw-normal-catlinks");
           if (catLinks != null) {
               Elements links = catLinks.getElementsByTag("ul");
               if (containsRoyalWords(links.get(0), Royal.wordsArrayforCatLinks)) {
                   return true;
               }
           }
       }
       return false;
   }

    private boolean isHuman() {

            String text = firstParagraph.text();
            if (text.contains(" born "))
                return true;


        return false;
    }

    private boolean belongsToRoyalFamily(Element e) {

            //the element is human, would like to check if this human belongs to the royal family
            if (hasField(e, "House", "House of Windsor")) {
                return true;
            }
            if (containsRoyalWords(e, Royal.wordsArray)) {
                return true;
            }

        return false;
    }

    private boolean containsRoyalWords(Element e, String[] wordsArray) {
        String textElement= e.text();
        for (String word: wordsArray) {
            if (textElement.toLowerCase().contains(word)) {
                return true;
            }
        }
        return false;
    }

    private boolean hasField (Element e, String fieldName) {
        Elements headers= e.getElementsByTag("th");
        for (Element h: headers) {
            if (h.text().equals(fieldName)) {
                return true;
            }
        }
        return false;
    }

    private boolean hasField (Element e, String fieldName, String value) {
        Elements headers= e.getElementsByTag("tr");
        for (Element h: headers) {
            if (h.getElementsByTag("th").text().equals(fieldName) && h.getElementsByTag("td").text().contains(value)) {
                return true;
            }
        }
        return false;
    }



    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        PageNode pageNode = (PageNode) o;

        //if (!links.equals(pageNode.links)) return false;
        if (!nodeId.equals(pageNode.nodeId)) return false;

        return true;
    }

    @Override
    public int hashCode() {
        int result = nodeId.hashCode();
        result = 31 * result + stringLinks.hashCode();
        return result;
    }

    public TreeMap<String, Integer> getWordsInPage() {
        return wordsInPage;
    }

    public int getPageLength() {
        return pageLength;
    }

    public String getNodeId() {
        return nodeId;
    }

    public Set<String> getStringLinks() {
        return stringLinks;
    }

    @Override
    public String toString() {
        return "url=" + nodeId + "; " + "numOfWords=" + pageLength;
    }

    public void addInPageNode(PageNode pageNode) {
        inPageNode.add(pageNode);
    }

    public void addOutPageNode(PageNode pageNode) {
        outPageNode.add(pageNode);
    }

    public Set<PageNode> getInPageNode() {
        return inPageNode;
    }

    public Set<PageNode> getOutPageNode() {
        return outPageNode;
    }

}
